{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-30T10:21:37.623679Z",
     "start_time": "2019-06-30T10:21:37.385149Z"
    }
   },
   "outputs": [],
   "source": [
    "from __future__ import print_function\n",
    "import sys\n",
    "import os\n",
    "import numpy as np\n",
    "import torch\n",
    "\n",
    "from edgeml_pytorch.graph.protoNN import ProtoNN\n",
    "from edgeml_pytorch.trainer.protoNNTrainer import ProtoNNTrainer\n",
    "import edgeml_pytorch.utils as utils\n",
    "import helpermethods as helper"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## USPS Data\n",
    "It is assumed that the USPS data has already been downloaded and set up with the help of `fetch_usps.py` and is placed in the `./usps10` subdirectory."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-30T10:21:37.643781Z",
     "start_time": "2019-06-30T10:21:37.626020Z"
    }
   },
   "outputs": [],
   "source": [
    "# Load data\n",
    "DATA_DIR = './usps10'\n",
    "train, test = np.load(DATA_DIR + '/train.npy'), np.load(DATA_DIR + '/test.npy')\n",
    "x_train, y_train = train[:, 1:], train[:, 0]\n",
    "x_test, y_test = test[:, 1:], test[:, 0]\n",
    "\n",
    "numClasses = max(y_train) - min(y_train) + 1\n",
    "numClasses = max(numClasses, max(y_test) - min(y_test) + 1)\n",
    "numClasses = int(numClasses)\n",
    "\n",
    "y_train = helper.to_onehot(y_train, numClasses)\n",
    "y_test = helper.to_onehot(y_test, numClasses)\n",
    "dataDimension = x_train.shape[1]\n",
    "numClasses = y_train.shape[1]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Model Parameters\n",
    "\n",
    "Note that ProtoNN is very sensitive to the value of the hyperparameter $\\gamma$, here stored in valiable GAMMA. If GAMMA is set to None, median heuristic will be used to estimate a good value of $\\gamma$ through the helper.getGamma() method. This method also returns the corresponding W and B matrices which should be used to initialize ProtoNN (as is done here)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-30T10:21:37.653094Z",
     "start_time": "2019-06-30T10:21:37.645811Z"
    }
   },
   "outputs": [],
   "source": [
    "PROJECTION_DIM = 60\n",
    "NUM_PROTOTYPES = 60\n",
    "REG_W = 0.000005\n",
    "REG_B = 0.0\n",
    "REG_Z = 0.00005\n",
    "SPAR_W = 0.8\n",
    "SPAR_B = 1.0\n",
    "SPAR_Z = 1.0\n",
    "LEARNING_RATE = 0.05\n",
    "NUM_EPOCHS = 200\n",
    "BATCH_SIZE = 32\n",
    "GAMMA = 0.0015"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-30T10:21:37.680649Z",
     "start_time": "2019-06-30T10:21:37.654776Z"
    }
   },
   "outputs": [],
   "source": [
    "W, B, gamma = helper.getGamma(GAMMA, PROJECTION_DIM, dataDimension,\n",
    "                       NUM_PROTOTYPES, x_train)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-30T10:21:37.700092Z",
     "start_time": "2019-06-30T10:21:37.682341Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Using x-entropy loss\n"
     ]
    }
   ],
   "source": [
    "protoNNObj = ProtoNN(dataDimension, PROJECTION_DIM, NUM_PROTOTYPES, numClasses,\n",
    "                     gamma, W=W, B=B)\n",
    "protoNNTrainer = ProtoNNTrainer(protoNNObj, REG_W, REG_B, REG_Z, SPAR_W, SPAR_B, SPAR_W,\n",
    "                                LEARNING_RATE, lossType='xentropy')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-30T10:25:13.443873Z",
     "start_time": "2019-06-30T10:21:37.702352Z"
    },
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 0 batch 0 loss 19.695423 acc 0.187500\n",
      "Epoch 1 batch 0 loss 2.124979 acc 0.406250\n",
      "Epoch 2 batch 0 loss 1.176522 acc 0.625000\n",
      "Epoch 3 batch 0 loss 0.852282 acc 0.781250\n",
      "Epoch 4 batch 0 loss 0.707985 acc 0.843750\n",
      "Epoch 5 batch 0 loss 0.620102 acc 0.906250\n",
      "Epoch 6 batch 0 loss 0.536484 acc 0.906250\n",
      "Epoch 7 batch 0 loss 0.506774 acc 0.906250\n",
      "Epoch 8 batch 0 loss 0.469522 acc 0.906250\n",
      "Epoch 9 batch 0 loss 0.443830 acc 0.937500\n",
      "Validation accuracy: 0.866467\n",
      "Epoch 10 batch 0 loss 0.418728 acc 0.937500\n",
      "Epoch 11 batch 0 loss 0.400192 acc 0.937500\n",
      "Epoch 12 batch 0 loss 0.391262 acc 0.937500\n",
      "Epoch 13 batch 0 loss 0.377898 acc 0.937500\n",
      "Epoch 14 batch 0 loss 0.374987 acc 0.937500\n",
      "Epoch 15 batch 0 loss 0.374003 acc 0.937500\n",
      "Epoch 16 batch 0 loss 0.372961 acc 0.937500\n",
      "Epoch 17 batch 0 loss 0.372122 acc 0.937500\n",
      "Epoch 18 batch 0 loss 0.374060 acc 0.937500\n",
      "Epoch 19 batch 0 loss 0.372302 acc 0.937500\n",
      "Validation accuracy: 0.890882\n",
      "Epoch 20 batch 0 loss 0.370008 acc 0.937500\n",
      "Epoch 21 batch 0 loss 0.368273 acc 0.937500\n",
      "Epoch 22 batch 0 loss 0.366527 acc 0.937500\n",
      "Epoch 23 batch 0 loss 0.366914 acc 0.937500\n",
      "Epoch 24 batch 0 loss 0.370677 acc 0.937500\n",
      "Epoch 25 batch 0 loss 0.370026 acc 0.937500\n",
      "Epoch 26 batch 0 loss 0.371454 acc 0.937500\n",
      "Epoch 27 batch 0 loss 0.375624 acc 0.937500\n",
      "Epoch 28 batch 0 loss 0.375046 acc 0.937500\n",
      "Epoch 29 batch 0 loss 0.376220 acc 0.937500\n",
      "Validation accuracy: 0.890384\n",
      "Epoch 30 batch 0 loss 0.376180 acc 0.937500\n",
      "Epoch 31 batch 0 loss 0.374452 acc 0.937500\n",
      "Epoch 32 batch 0 loss 0.373885 acc 0.937500\n",
      "Epoch 33 batch 0 loss 0.372436 acc 0.937500\n",
      "Epoch 34 batch 0 loss 0.372641 acc 0.937500\n",
      "Epoch 35 batch 0 loss 0.370789 acc 0.937500\n",
      "Epoch 36 batch 0 loss 0.370134 acc 0.937500\n",
      "Epoch 37 batch 0 loss 0.362332 acc 0.937500\n",
      "Epoch 38 batch 0 loss 0.358373 acc 0.937500\n",
      "Epoch 39 batch 0 loss 0.350356 acc 0.937500\n",
      "Validation accuracy: 0.896363\n",
      "Epoch 40 batch 0 loss 0.346920 acc 0.937500\n",
      "Epoch 41 batch 0 loss 0.341615 acc 0.937500\n",
      "Epoch 42 batch 0 loss 0.336807 acc 0.937500\n",
      "Epoch 43 batch 0 loss 0.336815 acc 0.937500\n",
      "Epoch 44 batch 0 loss 0.337165 acc 0.937500\n",
      "Epoch 45 batch 0 loss 0.338989 acc 0.937500\n",
      "Epoch 46 batch 0 loss 0.337964 acc 0.937500\n",
      "Epoch 47 batch 0 loss 0.336901 acc 0.937500\n",
      "Epoch 48 batch 0 loss 0.336525 acc 0.937500\n",
      "Epoch 49 batch 0 loss 0.335625 acc 0.937500\n",
      "Validation accuracy: 0.901345\n",
      "Epoch 50 batch 0 loss 0.335717 acc 0.937500\n",
      "Epoch 51 batch 0 loss 0.332490 acc 0.937500\n",
      "Epoch 52 batch 0 loss 0.331475 acc 0.937500\n",
      "Epoch 53 batch 0 loss 0.332839 acc 0.937500\n",
      "Epoch 54 batch 0 loss 0.333152 acc 0.937500\n",
      "Epoch 55 batch 0 loss 0.333211 acc 0.937500\n",
      "Epoch 56 batch 0 loss 0.331794 acc 0.937500\n",
      "Epoch 57 batch 0 loss 0.330032 acc 0.937500\n",
      "Epoch 58 batch 0 loss 0.329025 acc 0.937500\n",
      "Epoch 59 batch 0 loss 0.327888 acc 0.937500\n",
      "Validation accuracy: 0.904833\n",
      "Epoch 60 batch 0 loss 0.325547 acc 0.937500\n",
      "Epoch 61 batch 0 loss 0.325101 acc 0.937500\n",
      "Epoch 62 batch 0 loss 0.324205 acc 0.937500\n",
      "Epoch 63 batch 0 loss 0.322140 acc 0.937500\n",
      "Epoch 64 batch 0 loss 0.321153 acc 0.937500\n",
      "Epoch 65 batch 0 loss 0.320524 acc 0.937500\n",
      "Epoch 66 batch 0 loss 0.319890 acc 0.937500\n",
      "Epoch 67 batch 0 loss 0.318456 acc 0.937500\n",
      "Epoch 68 batch 0 loss 0.319331 acc 0.937500\n",
      "Epoch 69 batch 0 loss 0.319215 acc 0.937500\n",
      "Validation accuracy: 0.907324\n",
      "Epoch 70 batch 0 loss 0.318361 acc 0.937500\n",
      "Epoch 71 batch 0 loss 0.317372 acc 0.937500\n",
      "Epoch 72 batch 0 loss 0.315442 acc 0.937500\n",
      "Epoch 73 batch 0 loss 0.315069 acc 0.937500\n",
      "Epoch 74 batch 0 loss 0.315009 acc 0.937500\n",
      "Epoch 75 batch 0 loss 0.315125 acc 0.937500\n",
      "Epoch 76 batch 0 loss 0.313410 acc 0.937500\n",
      "Epoch 77 batch 0 loss 0.313252 acc 0.937500\n",
      "Epoch 78 batch 0 loss 0.312838 acc 0.937500\n",
      "Epoch 79 batch 0 loss 0.313598 acc 0.937500\n",
      "Validation accuracy: 0.909816\n",
      "Epoch 80 batch 0 loss 0.312934 acc 0.937500\n",
      "Epoch 81 batch 0 loss 0.312415 acc 0.937500\n",
      "Epoch 82 batch 0 loss 0.311750 acc 0.937500\n",
      "Epoch 83 batch 0 loss 0.311750 acc 0.937500\n",
      "Epoch 84 batch 0 loss 0.311120 acc 0.937500\n",
      "Epoch 85 batch 0 loss 0.312965 acc 0.937500\n",
      "Epoch 86 batch 0 loss 0.312122 acc 0.937500\n",
      "Epoch 87 batch 0 loss 0.312353 acc 0.937500\n",
      "Epoch 88 batch 0 loss 0.312312 acc 0.937500\n",
      "Epoch 89 batch 0 loss 0.312131 acc 0.937500\n",
      "Validation accuracy: 0.907823\n",
      "Epoch 90 batch 0 loss 0.312067 acc 0.937500\n",
      "Epoch 91 batch 0 loss 0.311683 acc 0.937500\n",
      "Epoch 92 batch 0 loss 0.312019 acc 0.937500\n",
      "Epoch 93 batch 0 loss 0.311767 acc 0.937500\n",
      "Epoch 94 batch 0 loss 0.311237 acc 0.937500\n",
      "Epoch 95 batch 0 loss 0.312212 acc 0.937500\n",
      "Epoch 96 batch 0 loss 0.311785 acc 0.937500\n",
      "Epoch 97 batch 0 loss 0.312252 acc 0.937500\n",
      "Epoch 98 batch 0 loss 0.310175 acc 0.937500\n",
      "Epoch 99 batch 0 loss 0.311505 acc 0.937500\n",
      "Validation accuracy: 0.907324\n",
      "Epoch 100 batch 0 loss 0.313065 acc 0.937500\n",
      "Epoch 101 batch 0 loss 0.314440 acc 0.937500\n",
      "Epoch 102 batch 0 loss 0.312527 acc 0.937500\n",
      "Epoch 103 batch 0 loss 0.311579 acc 0.937500\n",
      "Epoch 104 batch 0 loss 0.312305 acc 0.937500\n",
      "Epoch 105 batch 0 loss 0.307506 acc 0.937500\n",
      "Epoch 106 batch 0 loss 0.309213 acc 0.937500\n",
      "Epoch 107 batch 0 loss 0.309933 acc 0.937500\n",
      "Epoch 108 batch 0 loss 0.309595 acc 0.937500\n",
      "Epoch 109 batch 0 loss 0.309525 acc 0.937500\n",
      "Validation accuracy: 0.906826\n",
      "Epoch 110 batch 0 loss 0.309718 acc 0.937500\n",
      "Epoch 111 batch 0 loss 0.309883 acc 0.937500\n",
      "Epoch 112 batch 0 loss 0.309850 acc 0.937500\n",
      "Epoch 113 batch 0 loss 0.309907 acc 0.937500\n",
      "Epoch 114 batch 0 loss 0.309847 acc 0.937500\n",
      "Epoch 115 batch 0 loss 0.309618 acc 0.937500\n",
      "Epoch 116 batch 0 loss 0.308782 acc 0.937500\n",
      "Epoch 117 batch 0 loss 0.309182 acc 0.937500\n",
      "Epoch 118 batch 0 loss 0.308818 acc 0.937500\n",
      "Epoch 119 batch 0 loss 0.308587 acc 0.937500\n",
      "Validation accuracy: 0.908819\n",
      "Epoch 120 batch 0 loss 0.308455 acc 0.937500\n",
      "Epoch 121 batch 0 loss 0.307087 acc 0.937500\n",
      "Epoch 122 batch 0 loss 0.306889 acc 0.937500\n",
      "Epoch 123 batch 0 loss 0.306720 acc 0.937500\n",
      "Epoch 124 batch 0 loss 0.307359 acc 0.937500\n",
      "Epoch 125 batch 0 loss 0.307577 acc 0.937500\n",
      "Epoch 126 batch 0 loss 0.307461 acc 0.937500\n",
      "Epoch 127 batch 0 loss 0.307895 acc 0.937500\n",
      "Epoch 128 batch 0 loss 0.308021 acc 0.937500\n",
      "Epoch 129 batch 0 loss 0.307814 acc 0.937500\n",
      "Validation accuracy: 0.906826\n",
      "Epoch 130 batch 0 loss 0.308611 acc 0.937500\n",
      "Epoch 131 batch 0 loss 0.309282 acc 0.937500\n",
      "Epoch 132 batch 0 loss 0.308407 acc 0.937500\n",
      "Epoch 133 batch 0 loss 0.307874 acc 0.937500\n",
      "Epoch 134 batch 0 loss 0.308122 acc 0.937500\n",
      "Epoch 135 batch 0 loss 0.307364 acc 0.937500\n",
      "Epoch 136 batch 0 loss 0.309635 acc 0.937500\n",
      "Epoch 137 batch 0 loss 0.306114 acc 0.937500\n",
      "Epoch 138 batch 0 loss 0.306719 acc 0.937500\n",
      "Epoch 139 batch 0 loss 0.306940 acc 0.937500\n",
      "Validation accuracy: 0.908819\n",
      "Epoch 140 batch 0 loss 0.307175 acc 0.937500\n",
      "Epoch 141 batch 0 loss 0.306036 acc 0.937500\n",
      "Epoch 142 batch 0 loss 0.305179 acc 0.937500\n",
      "Epoch 143 batch 0 loss 0.302269 acc 0.937500\n",
      "Epoch 144 batch 0 loss 0.302065 acc 0.937500\n",
      "Epoch 145 batch 0 loss 0.301677 acc 0.937500\n",
      "Epoch 146 batch 0 loss 0.301558 acc 0.937500\n",
      "Epoch 147 batch 0 loss 0.301412 acc 0.937500\n",
      "Epoch 148 batch 0 loss 0.302211 acc 0.937500\n",
      "Epoch 149 batch 0 loss 0.301585 acc 0.937500\n",
      "Validation accuracy: 0.907823\n",
      "Epoch 150 batch 0 loss 0.301720 acc 0.937500\n",
      "Epoch 151 batch 0 loss 0.301118 acc 0.937500\n",
      "Epoch 152 batch 0 loss 0.300989 acc 0.937500\n",
      "Epoch 153 batch 0 loss 0.300585 acc 0.937500\n",
      "Epoch 154 batch 0 loss 0.300283 acc 0.937500\n",
      "Epoch 155 batch 0 loss 0.299906 acc 0.937500\n",
      "Epoch 156 batch 0 loss 0.300065 acc 0.937500\n",
      "Epoch 157 batch 0 loss 0.299933 acc 0.937500\n",
      "Epoch 158 batch 0 loss 0.299785 acc 0.937500\n",
      "Epoch 159 batch 0 loss 0.299569 acc 0.937500\n",
      "Validation accuracy: 0.908321\n",
      "Epoch 160 batch 0 loss 0.299688 acc 0.937500\n",
      "Epoch 161 batch 0 loss 0.300622 acc 0.937500\n",
      "Epoch 162 batch 0 loss 0.299520 acc 0.937500\n",
      "Epoch 163 batch 0 loss 0.299072 acc 0.937500\n",
      "Epoch 164 batch 0 loss 0.298733 acc 0.937500\n",
      "Epoch 165 batch 0 loss 0.298397 acc 0.937500\n",
      "Epoch 166 batch 0 loss 0.298402 acc 0.937500\n",
      "Epoch 167 batch 0 loss 0.298154 acc 0.937500\n",
      "Epoch 168 batch 0 loss 0.298369 acc 0.937500\n",
      "Epoch 169 batch 0 loss 0.298322 acc 0.937500\n",
      "Validation accuracy: 0.910314\n",
      "Epoch 170 batch 0 loss 0.299114 acc 0.937500\n",
      "Epoch 171 batch 0 loss 0.298441 acc 0.937500\n",
      "Epoch 172 batch 0 loss 0.298045 acc 0.937500\n",
      "Epoch 173 batch 0 loss 0.297733 acc 0.937500\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 174 batch 0 loss 0.297396 acc 0.937500\n",
      "Epoch 175 batch 0 loss 0.297138 acc 0.937500\n",
      "Epoch 176 batch 0 loss 0.296805 acc 0.937500\n",
      "Epoch 177 batch 0 loss 0.296536 acc 0.937500\n",
      "Epoch 178 batch 0 loss 0.296280 acc 0.937500\n",
      "Epoch 179 batch 0 loss 0.295992 acc 0.937500\n",
      "Validation accuracy: 0.910314\n",
      "Epoch 180 batch 0 loss 0.295781 acc 0.937500\n",
      "Epoch 181 batch 0 loss 0.295575 acc 0.937500\n",
      "Epoch 182 batch 0 loss 0.294182 acc 0.937500\n",
      "Epoch 183 batch 0 loss 0.294994 acc 0.937500\n",
      "Epoch 184 batch 0 loss 0.294937 acc 0.937500\n",
      "Epoch 185 batch 0 loss 0.294700 acc 0.937500\n",
      "Epoch 186 batch 0 loss 0.294113 acc 0.937500\n",
      "Epoch 187 batch 0 loss 0.292420 acc 0.937500\n",
      "Epoch 188 batch 0 loss 0.291916 acc 0.937500\n",
      "Epoch 189 batch 0 loss 0.291724 acc 0.937500\n",
      "Validation accuracy: 0.911310\n",
      "Epoch 190 batch 0 loss 0.291277 acc 0.937500\n",
      "Epoch 191 batch 0 loss 0.291130 acc 0.937500\n",
      "Epoch 192 batch 0 loss 0.291667 acc 0.937500\n",
      "Epoch 193 batch 0 loss 0.291187 acc 0.937500\n",
      "Epoch 194 batch 0 loss 0.290773 acc 0.937500\n",
      "Epoch 195 batch 0 loss 0.290442 acc 0.937500\n",
      "Epoch 196 batch 0 loss 0.290126 acc 0.937500\n",
      "Epoch 197 batch 0 loss 0.289970 acc 0.937500\n",
      "Epoch 198 batch 0 loss 0.289415 acc 0.937500\n",
      "Epoch 199 batch 0 loss 0.289136 acc 0.937500\n",
      "Validation accuracy: 0.913802\n"
     ]
    }
   ],
   "source": [
    "protoNNTrainer.train(BATCH_SIZE, NUM_EPOCHS, x_train, x_test, y_train, y_test, printStep=600, valStep=10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ExecuteTime": {
     "start_time": "2019-06-30T09:09:00.026Z"
    }
   },
   "source": [
    "## Evaluation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-06-30T10:25:13.469355Z",
     "start_time": "2019-06-30T10:25:13.447362Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Final test accuracy tensor(0.9138, dtype=torch.float64)\n",
      "Model size constraint (Bytes):  78240\n",
      "Number of non-zeros:  19560\n",
      "Actual model size:  78240\n",
      "Actual non-zeros:  16368\n"
     ]
    }
   ],
   "source": [
    " x_, y_= torch.Tensor(x_test), torch.Tensor(y_test)\n",
    "logits = protoNNObj.forward(x_)\n",
    "_, predictions = torch.max(logits, dim=1)\n",
    "_, target = torch.max(y_, dim=1)\n",
    "acc, count = protoNNTrainer.accuracy(predictions, target)\n",
    "W, B, Z, gamma  = protoNNObj.getModelMatrices()\n",
    "matrixList = [W, B, Z]\n",
    "matrixList = [x.detach().numpy() for x in matrixList]\n",
    "sparcityList = [SPAR_W, SPAR_B, SPAR_Z]\n",
    "nnz, size, sparse = helper.getModelSize(matrixList, sparcityList)\n",
    "print(\"Final test accuracy\", acc)\n",
    "print(\"Model size constraint (Bytes): \", size)\n",
    "print(\"Number of non-zeros: \", nnz)\n",
    "nnz, size, sparse = helper.getModelSize(matrixList, sparcityList,\n",
    "                                        expected=False)\n",
    "print(\"Actual model size: \", size)\n",
    "print(\"Actual non-zeros: \", nnz)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
